package bean;

import util.Connect;
import util.JdbcUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.NumberFormat;
import java.util.List;
import java.util.Map;

/**
 * @author dqy
 */
public class SingleConfig {
    @Ignore
    private static SingleConfig instance;

    private String price;
    private String activityReward;
    private String adviceReward;

    private SingleConfig() {
        // 通过反射还是能生成SingleConfig对象
        String sql = "select * from config where name='price' or name='activityReward' or name='adviceReward'";
        List<Map<String, String>> result = JdbcUtils.queryForMap(sql, null);
        for (Map<String, String> item : result) {
            String name = item.get("name");
            String value = item.get("value");
            if ("price".equals(name)) {
                price = value;
            } else if ("activityReward".equals(name)) {
                activityReward = value;
            } else if ("adviceReward".equals(name)) {
                adviceReward = value;
            }
        }
    }

    public static SingleConfig getInstance() {
        if (instance == null) {
            synchronized (SingleConfig.class) {
                if (instance == null) {
                    instance = new SingleConfig();
                }
            }
        }
        return instance;
    }

    public String getPrice() {
        return price;
    }

    private synchronized void setPrice(String price) {
        if (!updateConfig("price", price)) {
            throw new RuntimeException("Set Price failed.");
        }
        this.price = price;
        notifyLock();
    }

    public String getActivityReward() {
        return activityReward;
    }

    private synchronized void setActivityReward(String activityReward) {
        if (!updateConfig("activityReward", activityReward)) {
            throw new RuntimeException("Set ActivityReward failed.");
        }
        this.activityReward = activityReward;
    }

    public String getAdviceReward() {
        return adviceReward;
    }

    private synchronized void setAdviceReward(String adviceReward) {
        if (!updateConfig("adviceReward", adviceReward)) {
            throw new RuntimeException("Set AdviceReword failed.");
        }
        this.adviceReward = adviceReward;
    }

    private boolean updateConfig(String name, String value) {
        Connection conn = Connect.getInstance().getConnection();
        int r;
        try {
            conn.setAutoCommit(false);
            String sql = "UPDATE config SET value = ? WHERE name= ? ";
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, value);
            ps.setString(2, name);
            r = ps.executeUpdate();
            conn.commit();
        } catch (SQLException e) {
            try {
                conn.rollback();
            } catch (SQLException ignored) {
            }
            r = 0;
        }
        return r > 0;
    }

    private void notifyLock() {
        try {
            Socket socket = new Socket("localhost", 2017);
            NumberFormat format = NumberFormat.getInstance();
            format.setMinimumIntegerDigits(8);
            format.setMaximumFractionDigits(8);
            format.setGroupingUsed(false);
            byte[] data = new byte[9];
            data[0] = (byte) 0xFF;
            System.arraycopy(format.format(Float.parseFloat(this.price)).getBytes(), 0, data, 1, 8);
            OutputStream os = socket.getOutputStream();
            os.write(data);
            os.flush();
            os.close();
        } catch (IOException ignored) {
        }
    }

    public void set(String type, String value) throws Exception {
        switch (type) {
            case "price":
                setPrice(value);
                break;
            case "adviceReward":
                setAdviceReward(value);
                break;
            case "activityReward":
                setActivityReward(value);
                break;
            default:
                throw new Exception("No such config type");
        }
    }

    public Val val() {
        return new Val(price, activityReward, adviceReward);
    }

    @Override
    public String toString() {
        return new Val(price, activityReward, adviceReward).toString();
    }

    static final class Val {
        private final String price;
        private final String activityReward;
        private final String adviceReward;

        public Val(String price, String activityReward, String adviceReward) {
            this.price = price;
            this.activityReward = activityReward;
            this.adviceReward = adviceReward;
        }

        public final String getPrice() {
            return price;
        }

        public final String getActivityReward() {
            return activityReward;
        }

        public final String getAdviceReward() {
            return adviceReward;
        }

        @Override
        public String toString() {
            return "Config {" +
                "price='" + price + '\'' +
                ", activityReward='" + activityReward + '\'' +
                ", adviceReward='" + adviceReward + '\'' +
                '}';
        }
    }
}
